import React, { useState, useRef, useEffect, useCallback, useImperativeHandle, forwardRef } from 'react'
import './style.less'

type ScrollbarProps = {
  children: any
  always?: boolean
  onScroll?: React.ReactEventHandler
}

function Scrollbar ({ children, always = false, onScroll = () => false }: ScrollbarProps, ref: React.Ref<{initScrollbar: () => void}>) {
  const containerRef = useRef(null)
  const wrapperRef = useRef(null)
  const viewRef = useRef(null)
  // 滚动视图区尺寸
  const [wrapperWidth, setWrapperWidth] = useState(0)
  const [wrapperHeight, setWrapperHeight] = useState(0)
  // 比例
  const [scaleHorizontal, setScaleHorizontal] = useState(0)
  const [scaleVertical, setScaleVertical] = useState(0)
  // 滚动条bar尺寸
  const [horBarWidth, setHorBarWidth] = useState(0)
  const [verBarHeight, setVerBarHeight] = useState(0)
  // 是否展示各方向滚动条
  const [isShowHor, setIsShowHor] = useState(false)
  const [isShowVer, setIsShowVer] = useState(false)
  // 滚动距离
  const [scrollbarLeft, setScrollbarLeft] = useState(0)
  const [scrollbarTop, setScrollbarTop] = useState(0)

  /**
   * 各参数计算处理
   */
  const setSizeHandler = useCallback(() => {
    const container = containerRef.current as unknown as HTMLElement
    const view = viewRef.current as unknown as HTMLElement
    // 计算比例
    const scaleHorizontal = container.clientWidth / view.clientWidth
    const scaleVertical = container.clientHeight / view.clientHeight
    // 设置比例
    setScaleHorizontal(scaleHorizontal)
    setScaleVertical(scaleVertical)
    // 设置滚动视图区size
    setWrapperWidth(container.clientWidth)
    setWrapperHeight(container.clientHeight)
    // 设置滚动条bar尺寸
    setHorBarWidth(container.clientWidth * scaleHorizontal)
    setVerBarHeight(container.clientHeight * scaleVertical)
    // 判断是否展示滚动条
    setIsShowHor(view.clientWidth > container.clientWidth)
    setIsShowVer(view.clientHeight > container.clientHeight)
  }, [containerRef, viewRef])
  /**
   * 滚动视图scroll事件处理
   */
  const wrapperScrollHandler = (e: React.UIEvent<HTMLElement, Event>) => {
    const target = e.target as HTMLElement
    // 设置滚动距离
    setScrollbarLeft(target.scrollLeft * scaleHorizontal)
    setScrollbarTop(target.scrollTop * scaleVertical)
    onScroll(e)
  }
  /**
   * 鼠标拖拽
   */
  const dragScrollbarThumbHandler = (mouseDownEvent: React.MouseEvent<HTMLElement, MouseEvent>, dir: 'horizontal' | 'vertical') => {
    mouseDownEvent.preventDefault()
    mouseDownEvent.stopPropagation()
    const { offsetLeft, offsetTop } = mouseDownEvent.target as HTMLElement
    const wrapper = wrapperRef.current as unknown as HTMLElement
    let start = 0
    let startPosition = 0
    if (dir === 'horizontal') { // 横向
      start = mouseDownEvent.clientX
      startPosition =  offsetLeft
    } else if (dir === 'vertical') { // 纵向
      start = mouseDownEvent.clientY
      startPosition =  offsetTop
    }
    // 鼠标移动事件处理
    const moveHandler = (mouseMoveEvent: MouseEvent) => {
      const { clientX, clientY } = mouseMoveEvent
      mouseMoveEvent.preventDefault()
      if (dir === 'horizontal') { // 横向
        let currentLeft = clientX - start + startPosition
        // 边界检查
        if (currentLeft <= 0) {
          currentLeft = 0
        } else if (currentLeft >= wrapperWidth - horBarWidth) {
          currentLeft = wrapperWidth - horBarWidth
        }
        // 设置滚动条
        setScrollbarLeft(currentLeft)
        wrapper.scrollLeft = currentLeft / scaleHorizontal
      } else if (dir === 'vertical') { // 纵向
        let currentTop = clientY - start + startPosition
        // 边界检查
        if (currentTop <= 0) {
          currentTop = 0
        } else if (currentTop >= wrapperHeight - verBarHeight) {
          currentTop = wrapperHeight - verBarHeight
        }
        // 设置滚筒条
        setScrollbarTop(currentTop)
        wrapper.scrollTop = currentTop / scaleVertical
      }
    }
    document.addEventListener('mousemove', moveHandler)
    document.addEventListener('mouseup', () => {
      document.removeEventListener('mousemove', moveHandler)
    })
  }
  /**
   * 点击滚动条
   */
  const clickScrollbarHandler = (e: React.MouseEvent<HTMLElement, MouseEvent>, dir: 'horizontal' | 'vertical') => {
    const { offsetX, offsetY } = e.nativeEvent
    const wrapper = wrapperRef.current as unknown as HTMLElement
    if (dir === 'horizontal') { // 横向
      if (offsetX > horBarWidth + scrollbarLeft) {
        setScrollbarLeft(offsetX - horBarWidth)
        wrapper.scrollLeft = (offsetX - horBarWidth) / scaleHorizontal
      } else if (offsetX < scrollbarLeft) {
        setScrollbarLeft(offsetX)
        wrapper.scrollLeft = offsetX / scaleHorizontal
      }
    } else if (dir === 'vertical') { // 纵向
      if (offsetY > verBarHeight + scrollbarTop) {
        setScrollbarTop(offsetY - verBarHeight)
        wrapper.scrollTop = (offsetY - verBarHeight) / scaleVertical
      } else if (offsetY < scrollbarTop) {
        setScrollbarTop(offsetY)
        wrapper.scrollTop = offsetY / scaleVertical
      }
    }
  }
  // 向外暴露的方法
  useImperativeHandle(ref, () => ({
    initScrollbar: () => {
      setSizeHandler()
    }
  }))

  // 初始化
  useEffect(() => {
    setSizeHandler()
    window.addEventListener('resize', setSizeHandler)

    return () => {
      window.removeEventListener('resize', setSizeHandler)
    }
  }, [setSizeHandler])

  /**
   * 渲染视图
   */
  return (
    <div className="custom-scrollbar" ref={containerRef} onMouseEnter={setSizeHandler}>
      {/* 滚动容器 */}
      <div
        className="custom-scrollbar__wrapper"
        ref={wrapperRef}
        style={{
          width: wrapperWidth + 20,
          height: wrapperHeight + 20
        }}
        onScroll={(e) => wrapperScrollHandler(e)}
      >
        <div
          className="custom-scrollbar__view"
          ref={viewRef}
          style={{
            minWidth: wrapperWidth,
            minHeight: wrapperHeight
          }}
        >
          { children }
        </div>
      </div>
      {/* 横向滚动条 */}
      <div
        className={ always ? 'custom-scrollbar__bar is-horizontal is-always' : 'custom-scrollbar__bar is-horizontal' }
        style={{
          display: isShowHor ? '' : 'none',
          width: wrapperWidth
        }}
        onMouseDown={e => clickScrollbarHandler(e, 'horizontal')}
      >
        <span
          className="custom-scrollbar__thumb is-horizontal"
          onMouseDown={(e) => dragScrollbarThumbHandler(e, 'horizontal')}
          style={{
            left: scrollbarLeft,
            width: horBarWidth
          }}
        ></span>
      </div>
      {/* 纵向滚动条 */}
      <div
        className={ always ? 'custom-scrollbar__bar is-vertical is-always' : 'custom-scrollbar__bar is-vertical' }
        style={{
          display: isShowVer ? '' : 'none',
          height: wrapperHeight
        }}
        onMouseDown={e => clickScrollbarHandler(e, 'vertical')}
      >
        <span
          className="custom-scrollbar__thumb is-vertical"
          onMouseDown={(e) => dragScrollbarThumbHandler(e, 'vertical')}
          style={{
            top: scrollbarTop,
            height: verBarHeight
          }}
        ></span>
      </div>
    </div>
  )
}

export default forwardRef(Scrollbar)
